from datetime import datetime as dt
from hazard_value.topo import *
import warnings
import random
import numpy
import math
warnings.simplefilter('ignore',category=RuntimeWarning)

# uniform number generator

def uniform_generator (x) : return x

# random number generator

def random_generator (x): return int(random.uniform(1, c))

# compute the capacity for a graph g, a upper capacity bound c and a capacity function capacity

def compute_capacity (g, c, gen) :
	for index in range(len(g.es)): g.es[index]["capacity"] = gen(c)

# select the vertices of a datacenter graph g

def select_datacenter_vertices (g, f) :
	n = int(math.sqrt(len(g.vs)))
	vertices = list(range(n))
	return vertices

# select the cor vertices of a graph

def select_cor_vertices (g, f):
	return list(filter(lambda idx:g.vs[idx]["cor"], range(len(g.vs))))

# select the f max degreed vertices in the graph g

def select_max_degree_vertices (g, n) :
	degrees = {v:g.outdegree(v) for v in range(len(g.vs))}
	vertices = list(degrees.keys())
	vertices.sort(key = lambda v:degrees[v])
	vertices.reverse()
	if n!=numpy.inf: vertices = vertices[:n]
	return vertices

# select the f min degreed vertices in the graph g

def select_min_degree_vertices (g, n) :
	degrees = {v:g.outdegree(v) for v in range(len(g.vs))}
	vertices = list(degrees.keys())
	vertices.sort(key = lambda v:degrees[v])
	if n!=numpy.inf: vertices = vertices[:n]
	return vertices

# compute an uniform demand

def compute_demand (g, n, f, d, gen, select_vertices):

	vertices = select_vertices(g, n)
	D = dict()
	if d!=None : g["demand"] = d
	elif "demand" in g.attributes() : d = g["demand"]
	else : d = g["demand"] = 1
	flows = 0

	for s in vertices:
		for t in vertices:
			if s==t: continue
			D[s, t] = gen(d)
			flows += 1
			if flows==f: return D

	return D

# read the demand from a graph

def read_demand(g):
	edges = list(filter(lambda e:e["demand"]!=numpy.inf, g.es))
	D = {(edge.source, edge.target):int(edge["demand"]) for edge in edges}
	g.delete_edges(edges)
	g["demand"] = "input"
	return D

# write the demand to a graph

def write_demand(g, D):
	for (s, t) in D:
		edge = g.add_edge(s, t)
		edge["demand"] = D[s, t]

# compute the list of weights in a graph g under the failure scenario f

def compute_weights (g, f, weights=None) :
	W = numpy.full((len(g.vs), len(g.vs)), numpy.inf)
	for edge in g.es:
		if edge.index in f: edge["weight"] = numpy.inf
		else: W[edge.source, edge.target] = edge["weight"] = 1 if weights is None else weights[edge.source, edge.target]
	return W

# compute the list of weights in a graph g under the failure scenario f

def compute_weights_strategically (g, f, weights=None) :
	W = dict()
	for edge in g.es:
		if edge.index in f: W[edge.source, edge.target] = edge["weight"] = numpy.inf
		else: W[edge.source, edge.target] = edge["weight"] = 1 if weights is None else weights[edge.source, edge.target]
	return W

# compute random weight assignment

def compute_random_weights (g) :
	W = numpy.full((len(g.vs), len(g.vs)), numpy.inf)
	for edge in g.es:
		W[edge.source, edge.target] = int(random.uniform(1, len(g.es)))
	return W